// Upload 组件主文件
// 实现文件上传的核心功能，包括点击上传和拖拽上传

import React, { FC, useRef, ChangeEvent, useState } from 'react';
import axios from 'axios'; // HTTP 请求库，用于文件上传
import UploadList from './uploadList'; // 上传文件列表组件
import Dragger from './dragger'; // 拖拽上传区域组件

// 文件上传状态类型定义
export type UploadFileStatus = 'ready' | 'uploading' | 'success' | 'error';

// 上传文件对象接口
export interface UploadFile {
  uid: string; // 文件唯一标识
  size: number; // 文件大小
  name: string; // 文件名
  status?: UploadFileStatus; // 上传状态
  percent: number; // 上传进度百分比
  raw?: File; // 原始文件对象
  response?: any; // 服务器响应数据
  error?: any; // 错误信息
}

// Upload 组件属性接口
export interface UploadProps {
  /**必选参数, 上传的地址 */
  action: string;
  /**上传的文件列表,*/
  defaultFileList?: UploadFile[];
  /**上传文件之前的钩子，参数为上传的文件，若返回 false 或者 Promise 则停止上传。 */
  beforeUpload?: (file: File) => boolean | Promise<File>;
  /**文件上传时的钩子 */
  onProgress?: (percentage: number, file: UploadFile) => void;
  /**文件上传成功时的钩子 */
  onSuccess?: (data: any, file: UploadFile) => void;
  /**文件上传失败时的钩子 */
  onError?: (err: any, file: UploadFile) => void;
  /**文件状态改变时的钩子，上传成功或者失败时都会被调用	 */
  onChange?: (file: UploadFile) => void;
  /**文件列表移除文件时的钩子 */
  onRemove?: (file: UploadFile) => void;
  /**设置上传的请求头部 */
  headers?: { [key: string]: any };
  /**上传的文件字段名 */
  name?: string;
  /**上传时附带的额外参数 */
  data?: { [key: string]: any };
  /**支持发送 cookie 凭证信息 */
  withCredentials?: boolean;
  /**可选参数, 接受上传的文件类型 */
  accept?: string;
  /**是否支持多选文件 */
  multiple?: boolean;
  /**是否支持拖拽上传 */
  drag?: boolean;
  children?: React.ReactNode;
}

/**
 * Upload 组件 - 文件上传组件
 * 支持单文件和多文件上传，支持拖拽上传，支持上传进度显示
 * @param props - Upload 组件属性
 * @returns JSX.Element
 */
export const Upload: FC<UploadProps> = (props) => {
  const {
    action,
    defaultFileList,
    beforeUpload,
    onProgress,
    onSuccess,
    onError,
    onChange,
    onRemove,
    name = "file", // 默认文件字段名
    headers,
    data,
    withCredentials,
    accept,
    multiple,
    children,
    drag,
  } = props;

  // 文件输入框的引用
  const fileInput = useRef<HTMLInputElement>(null);
  // 文件列表状态
  const [fileList, setFileList] = useState<UploadFile[]>(defaultFileList || []);

  /**
   * 更新文件列表中指定文件的信息
   * @param updateFile - 要更新的文件对象
   * @param updateObj - 要更新的属性对象
   */
  const updateFileList = (
    updateFile: UploadFile,
    updateObj: Partial<UploadFile>
  ) => {
    setFileList((prevList) => {
      return prevList.map((file) => {
        if (file.uid === updateFile.uid) {
          return { ...file, ...updateObj }; // 合并更新属性
        } else {
          return file;
        }
      });
    });
  };

  /**
   * 处理点击上传区域事件
   * 触发隐藏的文件输入框点击事件
   */
  const handleClick = () => {
    if (fileInput.current) {
      fileInput.current.click();
    }
  };

  /**
   * 处理文件选择变化事件
   * @param e - 文件输入变化事件
   */
  const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files) {
      return; // 没有选择文件则返回
    }
    uploadFiles(files); // 处理选中的文件
    if (fileInput.current) {
      fileInput.current.value = ""; // 清空输入框，允许重复选择相同文件
    }
  };

  /**
   * 处理文件移除事件
   * @param file - 要移除的文件对象
   */
  const handleRemove = (file: UploadFile) => {
    setFileList((prevList) => {
      return prevList.filter((item) => item.uid !== file.uid); // 过滤掉指定文件
    });
    if (onRemove) {
      onRemove(file); // 调用移除回调函数
    }
  };

  /**
   * 处理文件上传（批量处理）
   * @param files - 文件列表
   * @param test - 是否为拖拽上传（调试用）
   */
  const uploadFiles = (files: FileList, test?: boolean) => {
    let postFiles = Array.from(files); // 将 FileList 转换为数组
    if (test) {
      console.log("drag", postFiles[0]); // 拖拽上传调试日志
    }

    // 遍历处理每个文件
    postFiles.forEach((file) => {
      if (!beforeUpload) {
        // 没有前置检查，直接上传
        post(file);
      } else {
        // 执行前置检查
        const result = beforeUpload(file);
        if (result && result instanceof Promise) {
          // 异步前置处理
          result.then((processedFile) => {
            post(processedFile);
          });
        } else if (result !== false) {
          // 同步前置处理通过
          post(file);
        }
        // 如果返回 false，则不上传
      }
    });
  };

  /**
   * 执行单个文件的上传操作
   * @param file - 要上传的文件
   */
  const post = (file: File) => {
    // 创建上传文件对象
    let _file: UploadFile = {
      uid: Date.now() + "upload-file", // 生成唯一ID
      status: "ready", // 初始状态为准备中
      name: file.name, // 文件名
      size: file.size, // 文件大小
      percent: 0, // 初始进度为0
      raw: file, // 原始文件对象
    };

    // 添加到文件列表开头
    setFileList((prevList) => {
      return [_file, ...prevList];
    });

    // 创建 FormData 对象
    const formData = new FormData();
    formData.append(name || "file", file); // 添加文件字段

    // 添加额外参数
    if (data) {
      Object.keys(data).forEach((key) => {
        formData.append(key, data[key]);
      });
    }

    // 发送上传请求
    axios
      .post(action, formData, {
        headers: {
          ...headers,
          "Content-Type": "multipart/form-data", // 设置内容类型
        },
        withCredentials, // 是否携带凭证
        onUploadProgress: (e) => {
          // 计算上传进度
          let percentage = Math.round((e.loaded * 100) / (e.total || 1)) || 0;
          if (percentage < 100) {
            // 更新上传进度
            updateFileList(_file, { percent: percentage, status: "uploading" });
            _file.status = "uploading";
            _file.percent = percentage;
            if (onProgress) {
              onProgress(percentage, _file); // 调用进度回调
            }
          }
        },
      })
      .then((resp) => {
        // 上传成功处理
        updateFileList(_file, { status: "success", response: resp.data });
        _file.status = "success";
        _file.response = resp.data;
        if (onSuccess) {
          onSuccess(resp.data, _file); // 调用成功回调
        }
        if (onChange) {
          onChange(_file); // 调用状态改变回调
        }
      })
      .catch((err) => {
        // 上传失败处理
        updateFileList(_file, { status: "error", error: err });
        _file.status = "error";
        _file.error = err;
        if (onError) {
          onError(err, _file); // 调用失败回调
        }
        if (onChange) {
          onChange(_file); // 调用状态改变回调
        }
      });
  };

  return (
    <div className="viking-upload-component">
      <div
        className="viking-upload-input"
        style={{ display: "inline-block" }}
        onClick={handleClick}
      >
        {/* 根据是否启用拖拽显示不同的上传区域 */}
        {drag ? (
          <Dragger
            onFile={(files) => {
              uploadFiles(files, true);
            }}
          >
            {children}
          </Dragger>
        ) : (
          children
        )}
        {/* 隐藏的文件输入框 */}
        <input
          className="viking-file-input"
          style={{ display: "none" }}
          ref={fileInput}
          onChange={handleFileChange}
          type="file"
          accept={accept} // 接受的文件类型
          multiple={multiple} // 是否支持多选
        />
      </div>

      {/* 上传文件列表组件 */}
      <UploadList fileList={fileList} onRemove={handleRemove} />
    </div>
  );
};

export default Upload;